import java.awt.*; import java.awt.font.*; import java.awt.geom.*; import java.awt.image.BufferedImage; import java.text.*; import java.util.*; import java.util.List; // resolves problem with java.awt.List and java.util.List import java.io.*; /** * A class that represents a picture. This class inherits from * SimplePicture and allows the student to add functionality to * the Picture class. * * */ public class Picture extends SimplePicture { ///////////////////// constructors ////////////////////////////////// /** * Constructor that takes no arguments */ public Picture () { /* not needed but use it to show students the implicit call to super() * child constructors always call a parent constructor */ super(); } /** * Constructor that takes a file name and creates the picture * @param fileName the name of the file to create the picture from */ public Picture(String fileName) { // let the parent class handle this fileName super(fileName); } /** * Constructor that takes the width and height * @param height the height of the desired picture * @param width the width of the desired picture */ public Picture(int height, int width) { // let the parent class handle this width and height super(width,height); } /** * Constructor that takes a picture and creates a * copy of that picture * @param copyPicture the picture to copy */ public Picture(Picture copyPicture) { // let the parent class do the copy super(copyPicture); } /** * Constructor that takes a buffered image * @param image the buffered image to use */ public Picture(BufferedImage image) { super(image); } ////////////////////// methods /////////////////////////////////////// /** * Method to return a string with information about this picture. * @return a string with information about the picture such as fileName, * height and width. */ public String toString() { String output = "Picture, filename " + getFileName() + " height " + getHeight() + " width " + getWidth(); return output; } /** Method to set the blue to 0 */ public void zeroBlue() { Pixel[][] pixels = this.getPixels2D(); for (Pixel[] rowArray : pixels) { for (Pixel pixelObj : rowArray) { pixelObj.setBlue(0); } } } public void keepOnlyBlue() { Pixel[][] pixels = this.getPixels2D(); for (Pixel[] rowArray : pixels) { for (Pixel pixelObj : rowArray) { pixelObj.setRed(0); pixelObj.setGreen(0); } } } public void keepOnlyGreen() { Pixel[][] pixels = this.getPixels2D(); for (Pixel[] rowArray : pixels) { for (Pixel pixelObj : rowArray) { pixelObj.setBlue(0); pixelObj.setRed(0); } } } public void keepOnlyRed() { Pixel[][] pixels = this.getPixels2D(); for (Pixel[] rowArray : pixels) { for (Pixel pixelObj : rowArray) { pixelObj.setBlue(0); pixelObj.setGreen(0); } } } public void negate() { Pixel[][] pixels = this.getPixels2D(); for (Pixel[] rowArray : pixels) { for (Pixel pixelObj : rowArray) { pixelObj.setRed(pixelObj.getRed() - 255); pixelObj.setGreen(pixelObj.getGreen() - 255); pixelObj.setBlue(pixelObj.getBlue() - 255); } } } public void grayscale() { Pixel[][] pixels = this.getPixels2D(); for (Pixel[] rowArray : pixels) { for (Pixel pixelObj : rowArray) { int avg = (int)((pixelObj.getRed() + pixelObj.getGreen() + pixelObj.getBlue()) / 3); pixelObj.setRed(avg); pixelObj.setBlue(avg); pixelObj.setGreen(avg); } } } public void invert() { Pixel[][] pixels = this.getPixels2D(); for (int row = 0; row < pixels.length; row++) { for (int col = 0; col < pixels[0].length; col++) { int red = 255 - pixels[row][col].getRed(); int green = 255 - pixels[row][col].getGreen(); int blue = 255 - pixels[row][col].getBlue(); Color newColor = new Color(red, green, blue); pixels[row][col].setColor(newColor); } } } public void darken(int amount) { Pixel[][] pixels = this.getPixels2D(); for (int row = 0; row < pixels.length; row++) { for (int col = 0; col < pixels[0].length; col++) { int red = pixels[row][col].getRed() - amount; int green = pixels[row][col].getGreen() - amount; int blue = pixels[row][col].getBlue() - amount; Color newColor = new Color(red, green, blue); pixels[row][col].setColor(newColor); } } } /** * Method to fix the fish * Takes a sample and adjust the image * to better see the fish */ public void fixUnderwater() { Pixel[][] pixels = this.getPixels2D(); int redAverage = 0; int greenAverage = 0; int blueAverage = 0; int totalPixels = 0; int maxRed = 0; int minRed = 255; int maxGreen = 0; int minGreen = 255; int maxBlue = 0; int minBlue = 255; // takes a sample from a fish and finds the average color value and range of colors for (int row = 26; row < 36; row++) { for (int col = 178; col < 198; col++) { totalPixels++; Pixel myPixel = pixels[row][col]; redAverage += myPixel.getRed(); greenAverage += myPixel.getGreen(); blueAverage += myPixel.getBlue(); if (myPixel.getRed() > maxRed) { maxRed = myPixel.getRed(); } if (myPixel.getRed() < minRed) { minRed = myPixel.getRed(); } if (myPixel.getGreen() > maxGreen) { maxGreen = myPixel.getGreen(); } if (myPixel.getGreen() < minGreen) { minGreen = myPixel.getGreen(); } if (myPixel.getBlue() > maxBlue) { maxBlue = myPixel.getBlue(); } if (myPixel.getGreen() < minBlue) { minBlue = myPixel.getBlue(); } } } redAverage = redAverage / totalPixels; greenAverage = greenAverage / totalPixels; blueAverage = blueAverage / totalPixels; Color averageColor = new Color(redAverage, greenAverage, blueAverage); // calculates the range int redRange = (maxRed - minRed); int greenRange = (maxGreen - minGreen); int blueRange = (maxBlue - minBlue); int redDistance = redRange; int greenDistance = greenRange; int blueDistance = blueRange; double maxDistance = Math.sqrt(redDistance * redDistance + greenDistance * greenDistance + blueDistance * blueDistance); double tolerance = 1.7; // higher tolerance means more pixels will be identified as "fish" // changes the image based on calculated distance from sample value for (int row = 0; row < pixels.length; row++) // Pixel[] rowArray : pixels) { for (int col = 0; col < pixels[0].length; col++) // Pixel pixelObj : rowArray) { Pixel myPixel = pixels[row][col]; // boolean closeEnough = myPixel.colorDistance(averageColor) < maxDistance * tolerance; // stopped here, define this*** // System.out.println(myPixel.colorDistance(averageColor)); if (closeEnough) { myPixel.setBlue(myPixel.getBlue() + 50); } else { myPixel.setBlue(myPixel.getBlue() - 50); } } } } /** Method that mirrors the picture around a * vertical mirror in the center of the picture * from left to right */ public void mirrorVertical() { Pixel[][] pixels = this.getPixels2D(); Pixel leftPixel = null; Pixel rightPixel = null; int width = pixels[0].length; for (int row = 0; row < pixels.length; row++) { for (int col = 0; col < width / 2; col++) { leftPixel = pixels[row][col]; rightPixel = pixels[row][width - 1 - col]; rightPixel.setColor(leftPixel.getColor()); } } } public void mirrorVerticalRightToLeft() { Pixel[][] pixels = this.getPixels2D(); Pixel leftPixel = null; Pixel rightPixel = null; int width = pixels[0].length; for (int row = 0; row < pixels.length; row++) { for (int col = 0; col < width / 2; col++) { leftPixel = pixels[row][col]; rightPixel = pixels[row][width - 1 - col]; leftPixel.setColor(rightPixel.getColor()); } } } public void mirrorHorizontal() { Pixel[][] pixels = this.getPixels2D(); Pixel topPixel = null; Pixel bottomPixel = null; int height = pixels.length; for (int row = 0; row < height; row++) { for (int col = 0; col < pixels[0].length; col++) { topPixel = pixels[row][col]; bottomPixel = pixels[height - 1 - row][col]; bottomPixel.setColor(topPixel.getColor()); } } } public void mirrorHorizontalBottomToTop() { Pixel[][] pixels = this.getPixels2D(); Pixel topPixel = null; Pixel bottomPixel = null; int height = pixels.length; for (int row = 0; row < height; row++) { for (int col = 0; col < pixels[0].length; col++) { topPixel = pixels[row][col]; bottomPixel = pixels[height - 1 - row][col]; topPixel.setColor(bottomPixel.getColor()); } } } public void mirrorDiagonal() // mirrors from top right to bottom left { Pixel[][] pixels = this.getPixels2D(); Pixel topRightPixel = null; Pixel bottomLeftPixel = null; int maxLength; if (pixels.length < pixels[0].length) { maxLength = pixels.length; } else {maxLength = pixels[0].length; } for (int row = 0; row < maxLength; row++) { for (int col = row; col < maxLength; col++) { topRightPixel = pixels[row][col]; bottomLeftPixel = pixels[col][row]; bottomLeftPixel.setColor(topRightPixel.getColor()); } } } /** Mirror just part of a picture of a temple */ public void mirrorTemple() { int mirrorPoint = 276; Pixel leftPixel = null; Pixel rightPixel = null; int count = 0; Pixel[][] pixels = this.getPixels2D(); // loop through the rows for (int row = 27; row < 97; row++) { // loop from 13 to just before the mirror point for (int col = 13; col < mirrorPoint; col++) { count++; leftPixel = pixels[row][col]; rightPixel = pixels[row] [mirrorPoint - col + mirrorPoint]; rightPixel.setColor(leftPixel.getColor()); } } System.out.println(count); } /** Mirrors the arms of the snowman */ public void mirrorArms() { int mirrorPoint = 193; Pixel topPixel = null; Pixel bottomPixel = null; Pixel[][] pixels = this.getPixels2D(); // Left arm for (int row = 158; row < mirrorPoint; row++) { // loop from 13 to just before the mirror point for (int col = 103; col < 170; col++) { topPixel = pixels[row][col]; bottomPixel = pixels[mirrorPoint - row + mirrorPoint][col]; bottomPixel.setColor(topPixel.getColor()); } } int mirrorPoint2 = 198; Pixel topPixel2 = null; Pixel bottomPixel2 = null; // Right arm for (int row = 171; row < mirrorPoint2; row++) { // loop from 13 to just before the mirror point for (int col = 239; col < 294; col++) { topPixel2 = pixels[row][col]; bottomPixel2 = pixels[mirrorPoint2 - row + mirrorPoint2][col]; bottomPixel2.setColor(topPixel2.getColor()); } } } public void mirrorGull() { int mirrorPoint = 345; Pixel rightPixel = null; Pixel leftPixel = null; Pixel[][] pixels = this.getPixels2D(); // Seagull for (int row = 235; row < 323; row++) { for (int col = 238; col < mirrorPoint; col++) { rightPixel = pixels[row][col]; leftPixel = pixels[row][mirrorPoint - col + mirrorPoint/3]; leftPixel.setColor(rightPixel.getColor()); } } } /** copy from the passed fromPic to the * specified startRow and startCol in the * current picture * @param fromPic the picture to copy from * @param startRow the start row to copy to * @param startCol the start col to copy to */ public void copy(Picture fromPic, int startRow, int startCol) { Pixel fromPixel = null; Pixel toPixel = null; Pixel[][] toPixels = this.getPixels2D(); Pixel[][] fromPixels = fromPic.getPixels2D(); for (int fromRow = 0, toRow = startRow; fromRow < fromPixels.length && toRow < toPixels.length; fromRow++, toRow++) { for (int fromCol = 0, toCol = startCol; fromCol < fromPixels[0].length && toCol < toPixels[0].length; fromCol++, toCol++) { fromPixel = fromPixels[fromRow][fromCol]; toPixel = toPixels[toRow][toCol]; toPixel.setColor(fromPixel.getColor()); System.out.println("Fromrow " + fromRow + " toRow " + toRow + " fromCol" + fromCol + " toCol " + toCol); } } } public void copy2(Picture fromPic, int startRow, int endRow, int startCol, int endCol) { Pixel fromPixel = null; Pixel toPixel = null; Pixel[][] toPixels = this.getPixels2D(); Pixel[][] fromPixels = fromPic.getPixels2D(); for (int fromRow = 0, toRow = startRow; fromRow < fromPixels.length && toRow < endRow; fromRow++, toRow++) { for (int fromCol = 0, toCol = startCol; fromCol < fromPixels[0].length && toCol < endCol; fromCol++, toCol++) { fromPixel = fromPixels[fromRow][fromCol]; toPixel = toPixels[toRow][toCol]; toPixel.setColor(fromPixel.getColor()); //System.out.println("Fromrow " + fromRow + " toRow " + toRow + " fromCol" + fromCol + " toCol " + toCol); //System.out.println(fromPixels[0].length); } } } /** Method to create a collage of several pictures */ public void createCollage() { Picture flower1 = new Picture("flower1.jpg"); Picture flower2 = new Picture("flower2.jpg"); //this.copy(flower1,100,0); this.copy2(flower1,0,100, 0, 100); //flower1.explore(); //this.copy(flower1,200,0); // Mirroring int mirrorPoint = 98; Pixel rightPixel = null; Pixel leftPixel = null; Pixel[][] pixels = this.getPixels2D(); for (int row = 0; row < 98; row++) { for (int col = 0; col < 88; col++) { rightPixel = pixels[row][col]; leftPixel = pixels[mirrorPoint - row + mirrorPoint][col]; leftPixel.setColor(rightPixel.getColor()); } } Picture flowerNoBlue = new Picture(flower2); flowerNoBlue.zeroBlue(); this.copy2(flowerNoBlue,300,350,80,500); Picture flowerinverse = new Picture(flower2); flowerinverse.invert(); this.copy2(flowerinverse, 100, 300, 80, 300); //this.copy(flowerNoBlue,300,0); //this.copy(flower1,400,0); //this.copy(flower2,500,0); //this.mirrorVertical(); this.write("collage.jpg"); } public void myCollage() { Picture flower1 = new Picture("flower1.jpg"); this.copy2(flower1,10,20, 0, 100); this.write("mycollage.jpg"); } /** Method to show large changes in color * @param edgeDist the distance for finding edges */ public void edgeDetection(int edgeDist) { Pixel leftPixel = null; Pixel rightPixel = null; Pixel topPixel = null; Pixel bottomPixel = null; Pixel[][] pixels = this.getPixels2D(); for (int row = 0; row < pixels.length - 1; row++) { for (int col = 0; col < pixels[0].length-1; col++) { leftPixel = pixels[row][col]; rightPixel = pixels[row][col+1]; topPixel = pixels[row][col]; bottomPixel = pixels[row + 1][col]; if (leftPixel.colorDistance(rightPixel.getColor()) > edgeDist || topPixel.colorDistance(bottomPixel.getColor()) > edgeDist) leftPixel.setColor(Color.BLACK); else leftPixel.setColor(Color.WHITE); } } } /** * Method for edge detection, my way. * Handy methods used in this method are below. * * * */ public void edgeDetection2(int edgeDist) { Pixel currentPixel = null, testPixel = null; // width and height of pixel cluster int testWidth = 3; int testHeight = 3; Pixel[][] pixels = this.getPixels2D(); double[][] edgeAngle = new double[Math.round(pixels.length / testHeight)][Math.round(pixels[0].length / testWidth)]; // the 2D array edgeAngle will store the perceived angle (in radians) of the edge for (int row = 0; row < pixels.length - testHeight; row += testHeight) { for (int col = 0; col < pixels[0].length - testWidth; col += testWidth) { Pixel[][] currentPixels = this.getPixelCluster(pixels, row, col, testWidth, testHeight); double greatestDistance = -10; double greatestAngle = -1; // not actually the greatest value, but corresponding to greatest distance // I may need to determin which of the two partial clusters is darker, for filling in purposes for (double angle = 0; angle < Math.PI + 0.1; angle += Math.PI / 8) { ArrayList<Pixel> group1 = this.getPartialArray(currentPixels, angle, 0); Color group1Color = this.getAverageColor(this.getPixelColors(group1)); ArrayList<Pixel> group2 = this.getPartialArray(currentPixels, angle, 1); Color group2Color = this.getAverageColor(this.getPixelColors(group2)); double currentColorDistance = this.colorDistance(group1Color, group2Color); if (currentColorDistance > greatestDistance) { greatestDistance = currentColorDistance; greatestAngle = angle; } } greatestAngle = Math.round(greatestAngle * 100.0) / 100.0; edgeAngle[row / testHeight][col / testWidth] = greatestAngle; if (greatestDistance > edgeDist) { ArrayList<Pixel> group1 = this.getPartialArray(currentPixels, greatestAngle, 0); ArrayList<Pixel> group2 = this.getPartialArray(currentPixels, greatestAngle, 1); for (Pixel pixel : group1) { pixel.setColor(Color.BLACK); } for (Pixel pixel : group2) { pixel.setColor(Color.WHITE); } } else { for (Pixel[] pixelArray : currentPixels) { for (Pixel pixel : pixelArray) { pixel.setColor(Color.WHITE); } } } } } File file = new File("outputAngles.txt"); try{ PrintWriter writer = new PrintWriter(file, "UTF-8"); for (int row = 0; row < edgeAngle.length; row++) { for (int col = 0; col < edgeAngle[0].length; col++) { writer.print(edgeAngle[row][col]); writer.print(" "); } writer.print("\n"); } writer.close(); } catch(Exception e){ e.printStackTrace(); } } public double colorDistance(Color testColor1, Color testColor2) { double redDistance = testColor2.getRed() - testColor1.getRed(); double greenDistance = testColor2.getGreen() - testColor1.getGreen(); double blueDistance = testColor2.getBlue() - testColor1.getBlue(); double distance = Math.sqrt(redDistance * redDistance + greenDistance * greenDistance + blueDistance * blueDistance); return distance; } public Color getAverageColor(Color[] myColors) { int totalRed = 0; int totalGreen = 0; int totalBlue = 0; int total = 0; for (Color currentColor : myColors) { totalRed += currentColor.getRed(); totalGreen += currentColor.getGreen(); totalBlue += currentColor.getBlue(); total++; } return new Color(totalRed / total, totalGreen / total, totalBlue / total); } public Color[] getPixelColors(ArrayList<Pixel> pixels) { Color[] myColors = new Color[pixels.size()]; int count = 0; for (Pixel currentPixel : pixels) { myColors[count] = currentPixel.getColor(); count++; } return myColors; } public Pixel[][] getPixelCluster(Pixel[][] pixels, int startRow, int startCol, int width, int height) { Pixel[][] pixelCluster = new Pixel[height][width]; if (pixels.length < startRow + height || pixels[0].length < startCol + width) { return pixelCluster; } for (int row = startRow; row < startRow + height; row++) { for (int col = startCol; col < startCol + width; col++) { pixelCluster[row - startRow][col - startCol] = pixels[row][col]; } } return pixelCluster; } /** * Method getPartialArray takes an array of pixels, * an angle to divide the array, and the "type of" that * determines whether it returns the top/right (0) or * the bottom/left (1) * * This one only takes the elements that lie on the line of division * * Need to update this method to match the one below ******** * * @param pixels * @param angle the angle to divide, given in radians (0 to pi) */ public ArrayList<Pixel> getPartialArrayLine(Pixel[][] fullArray, double angle, int typeOf) { int rows = fullArray.length, cols = fullArray[0].length; int centerRow = rows / 2, centerCol = cols / 2; int arrayLength = (rows * cols); ArrayList<Pixel> tempList = new ArrayList<Pixel>(); double slope = Math.tan(angle); if (slope == 0) slope = 0.001; double newSlope = -1 / slope; for (int i = 0; i < arrayLength; i++) { int currentRow = i / cols, currentCol = i % cols; //System.out.println(i + "\t" + currentRow + " " + currentCol); if (currentCol == (newSlope * (currentRow - centerRow) + centerCol)) // this tests whether the current cell is above/below the "line" { if (typeOf == 1) { tempList.add(fullArray[currentRow][currentCol]); //System.out.println("added " + i); } } else { if (typeOf == 0) { tempList.add(fullArray[currentRow][currentCol]); //System.out.println("added " + i); } } } return tempList; } /** * Method getPartialArray takes an array of pixels, * an angle to divide the array, and the "type of" that * determines whether it returns the top/right (0) or * the bottom/left (1) * * Need to update this method to match the one below ******** * * @param pixels * @param angle the angle to divide, given in radians (0 to pi) */ public ArrayList<Pixel> getPartialArray(Pixel[][] fullArray, double angle, int typeOf) { int rows = fullArray.length, cols = fullArray[0].length; int centerRow = rows / 2, centerCol = cols / 2; int arrayLength = (rows * cols); ArrayList<Pixel> tempList = new ArrayList<Pixel>(); double slope = Math.tan(angle); if (slope == 0) slope = 0.001; double newSlope = -1 / slope; for (int i = 0; i < arrayLength; i++) { int currentRow = i / cols, currentCol = i % cols; //System.out.println(i + "\t" + currentRow + " " + currentCol); if (currentCol < (newSlope * (currentRow - centerRow) + centerCol)) // this tests whether the current cell is above/below the "line" { if (typeOf == 1) { tempList.add(fullArray[currentRow][currentCol]); //System.out.println("added " + i); } } else { if (typeOf == 0) { tempList.add(fullArray[currentRow][currentCol]); //System.out.println("added " + i); } } } return tempList; } public static ArrayList<Integer> getPartialArray(int[][] fullArray, double angle, int typeOf) { int rows = fullArray.length, cols = fullArray[0].length; int centerRow = rows / 2, centerCol = cols / 2; int arrayLength = (rows * cols); ArrayList<Integer> tempList = new ArrayList<Integer>(); double slope = Math.tan(angle); if (slope == 0) slope = 0.001; double newSlope = -1 / slope; for (int i = 0; i < arrayLength; i++) { int currentRow = i / cols, currentCol = i % cols; System.out.println(i + "\t" + currentRow + " " + currentCol); if (currentCol < (newSlope * (currentRow - centerRow) + centerCol)) { if (typeOf == 1) { tempList.add(fullArray[currentRow][currentCol]); System.out.println("added " + i); } } else { if (typeOf == 0) { tempList.add(fullArray[currentRow][currentCol]); System.out.println("added " + i); } } } return tempList; } /* Main method for testing - each class in Java can have a main * method */ public static void main(String[] args) { Picture beach = new Picture("caterpillar.jpg"); beach.explore(); beach.zeroBlue(); beach.explore(); } } // this } is the end of class Picture, put all new methods before this